
#include <string>
#include <sstream>
#include <fstream>
#include <errno.h>
#include <assert.h>

#include "parser.h"
#include "helper.hpp"
#include "tty_io.hpp"

extern std::map<std::string, Base*> gids;
extern std::map<std::string, Serial_info> port_map;
extern std::map<std::string, Panel> panels;
extern std::map<std::string, Sensor> sensors;
extern std::map<std::string, Command> commands;
extern std::map<std::string, Mode> modes;

extern int gsm_fd;

/* GSM */
int write_to_gsm(char *chars) 
{
    int len = strlen(chars);
    // stick a after the command        (0xd == 13 == ASCII CR)
    chars[len] = 0x0d;

    // terminate the string properly
    chars[len+1] = 0x00;
    int n = write(gsm_fd, chars, strlen(chars));
    if (n < 0) {
        fputs("write failed!\n", stderr);
        return -1;
    }

    return 0;
}

int read_from_gsm(char *result) 
{
    int n = read(gsm_fd, result, 254);

    result[n-1] = 0x00; 
    if (n <= 0) {
        if (errno == EAGAIN) {
            logger("SERIAL EAGAIN ERROR\n");
            return -1;
        } else {
            logger("SERIAL read error %d %s\n", errno, strerror(errno));
            return -1;
        }
    }

    return 0;
}

//TODO:
double eval_expr2(std::string expr)
{
    Parser eval;
    double eval_result;
    std::vector<std::string> tmp;

    extract_gids(expr, tmp);
    replace_timers(expr);
    find_and_replace(expr, "on", "1");
    find_and_replace(expr, "off", "0");

    /* find and replace g* string with it's value. */
    for(std::vector<std::string>::iterator g_it = tmp.begin(); 
            g_it < tmp.end(); ++g_it){
        std::map<std::string, Base*>::iterator b_it;
        b_it = gids.find(*g_it);
        if(b_it != gids.end()){
            find_and_replace(expr, *g_it, gids[*g_it]->get());
        } else {
            logger("wrong 'gid' specified in ture/false\n");
            continue;
        }
    }

    eval_result = eval.parse(expr.c_str());
    return eval_result;
}

double eval_expr(std::string expr, std::string cnd_value)
{
    Parser eval;
    double eval_result;
    std::vector<std::string> tmp;

    extract_gids(expr, tmp);
    //TODO:
    replace_timers(expr);
    find_and_replace(expr, "value", cnd_value);
    find_and_replace(expr, "off", "0");
    find_and_replace(expr, "on", "1");

    /* find and replace g* string with it's value. */
    for(std::vector<std::string>::iterator g_it = tmp.begin(); 
            g_it < tmp.end(); ++g_it){
        std::map<std::string, Base*>::iterator b_it;
        b_it = gids.find(*g_it);
        if(b_it != gids.end()){
            find_and_replace(expr, *g_it, gids[*g_it]->get());
        } else {
            logger("wrong 'gid' specified in ture/false\n");
            continue;
        }
    }

    eval_result = eval.parse(expr.c_str());
    return eval_result;
}

/* Execute commands */
void exec(Commands_info &cmds, Condition &c)
{
    double eval_result;
    std::vector< std::pair<std::string, std::string> > cmd;

    if(cmds.type == SAMPLE){
        cmd = cmds.sample;
    } else {
        eval_result = eval_expr(cmds.expr, c.m->get_value());
        if(1 == (int)eval_result){
            cmd = cmds.if_true;
        } else {
            cmd = cmds.if_false;
        }
    }

    for(size_t i = 0; i < cmd.size(); i++) {
        //logger("gid = [%s], [%s]\n", cmd[i].first.c_str(), cmd[i].second.c_str());
        if(cmd[i].second[0] == '('){
            eval_result = eval_expr(cmd[i].second, c.m->get_value());
            //logger("GID = [%s], VALUE = [%s], RESULT = %.01f\n",
            //cmd[i].first.c_str(), cmd[i].second.c_str(), eval_result);
            gids[cmd[i].first]->set(to_string((double)eval_result));
        } else {
            //logger("GID = [%s], VALUE = [%s], EXPR_CND = %s\n",
            //cmd[i].first.c_str(), cmd[i].second.c_str(), c.expr.c_str());
            gids[cmd[i].first]->set(cmd[i].second);
        }
    }
}

/* Print the 'response' vector */
void print_resp(std::vector<uint8_t> resp)
{   
    std::string buff;
    for(size_t i = 0; i < resp.size(); ++i){
        buff += to_string(resp[i]) + " ";
    }
    logger(buff.c_str());
}

/* Tokenize the string */
void tokenize(const std::string& str,  const std::string& delimiters,
        std::vector<std::string>& tokens)
{
    tokens.clear();
    // Skip delimiters at beginning.
    string::size_type lastPos = str.find_first_not_of(delimiters, 0);
    // Find first "non-delimiter".
    string::size_type pos     = str.find_first_of(delimiters, lastPos);

    while (string::npos != pos || string::npos != lastPos)
    {
        // Found a token, add it to the vector.
        tokens.push_back(str.substr(lastPos, pos - lastPos));
        // Skip delimiters.  Note the "not_of"
        lastPos = str.find_first_not_of(delimiters, pos);
        // Find next "non-delimiter"
        pos = str.find_first_of(delimiters, lastPos);
    }
}

/* Find specified old_str in the str and replace it to new_str. */
void find_and_replace(std::string& str, const std::string& old_str, 
        std::string new_str = "")
{
    size_t pos = 0;
    while((pos = str.find(old_str, pos)) != std::string::npos) {
        str.replace(pos, old_str.length(), new_str);
        pos += new_str.length();
    }
}

/* Extract "g*" string from specified str. */
void extract_gids(const std::string str, std::vector<std::string>& cmds)
{
    size_t start, end;
    start = str.find("g");
    while(start != std::string::npos){
        end = str.find_first_not_of("0123456789", start + 1);
        if(end != std::string::npos){
            cmds.push_back(str.substr(start, end - start));
        } else {
            cmds.push_back(str.substr(start));
            return;
        }
        start = str.find("g", end);
    }
    return;
}

void replace_timers(std::string& s)
{
    time_t t;
    struct tm tm;
    struct timeb tp;
    long double sec_since;
    double mili_float;

    ftime(&tp);
    t = tp.time;
    mili_float = tp.millitm;
    sec_since = t + mili_float / 1000;
    tm = *localtime(&t);
    //logger("sec_since = %llf\n", sec_since);

    find_and_replace(s, "year", to_string(tm.tm_year + 1900));
    find_and_replace(s, "wday", to_string(tm.tm_wday + 1));
    find_and_replace(s, "mon",  to_string(tm.tm_mon + 1));
    find_and_replace(s, "day",  to_string(tm.tm_mday));
    find_and_replace(s, "hour", to_string(tm.tm_hour));
    find_and_replace(s, "min",  to_string(tm.tm_min));
    find_and_replace(s, "sec",  to_string(tm.tm_sec));
    find_and_replace(s, "mili", to_string(tp.millitm));
    find_and_replace(s, "now",  to_string(sec_since));
}

/* Handle client requests. */
std::string handle_request(std::string request)
{   
    char req_type;
    std::string gid;
    std::string response;
    std::vector<std::string> ids;
    std::map<std::string, Base*>::iterator b_it;

    sscanf(request.c_str(), "%c:", &req_type);
    request.erase(request.find_last_not_of(" \n\r\t")+1);
    tokenize(request, ":", ids);
    switch(req_type) {
        case 'g':
            for(size_t i = 1; i < ids.size(); i++){
                gid = "g" + ids[i];
                b_it = gids.find(gid);
                if(b_it == gids.end()){
                    response = "GET: error, wrong gid, gid=" + gid;
                    break;
                }

                if(i + 1 == ids.size()){
                    response = response + gids[gid]->get();
                } else {
                    response = response + gids[gid]->get() + ":";
                }
            }
            break;
        case 's':
            if(ids.size() != 3){
                response = "SET: syntax error";
                break;
            }
            gid = "g" + ids[1];
            b_it = gids.find(gid);
            if(b_it == gids.end()){
                response = "SET: error, wrong gid, gid=" + gid;
            } else {
                gids[gid]->set(ids[2]);
                response = "SET: " + gid + " = " + ids[2];
            }
            break;
        default:
            response = "ERROR: unknown request";
    }

    return response;
}

/* Parsing tty.conf serial port config file */
void parse_serial_config(std::string file_name)
{
    std::string port;
    std::ifstream tty_config_name(file_name.c_str());

    while (std::getline(tty_config_name, port)) {
        port_map[port].s_fd = init_tty(port);
        pthread_mutex_init(&port_map[port].s_mutex, NULL);
        logger("Successfully initialized %s, FD = %d\n", port.c_str(), port_map[port].s_fd);
    }
}

void init_and_start_core()
{
    /* Initializing all the panel's Sensors. */
    logger("Initializing Panels\n");
    typedef std::map<std::string, Panel>::iterator it_type;
    for(it_type it = panels.begin(); it != panels.end(); it++) {
        it->second.init();
        it->second.update();
    }

    /* Initializing all the Command's values. */
    logger("Initializing Commands\n");
    typedef std::map<std::string, Command>::iterator cmd_type;
    for(cmd_type it = commands.begin(); it != commands.end(); it++) {
        if(!it->second.data_for_get.empty()){
            it->second.init();
        }
    }

    logger("Starting Modes\n");
    typedef std::map<std::string, Mode>::iterator m_type;
    for(m_type it = modes.begin(); it != modes.end(); it++) {
        if(it->second.state == "on"){
            it->second.start();
        }
    }
}

uint8_t calculate_csum(const b_packet_t *pkt)
{
    uint8_t csum = 0;
    uint8_t *ptr = (uint8_t*)pkt;
    int size = pkt->size + sizeof(b_packet_t);

    for(int i = 0; i < size - 1; ++i) {
        csum += *ptr++;
    }

    return csum;
}

b_packet_t* str_to_pkt(const std::string str)
{
    size_t size;
    uint8_t  *ptr;
    b_packet_t *pkt;
    std::vector<std::string> tokens;

    tokenize(str, " ", tokens);
    size = tokens.size() + 1; // +1 for csum

    //Error: wrong packet size TODO: for all asserts
    assert(size > sizeof(b_packet_t)); 

    pkt = (b_packet_t*)malloc(size);
    assert(pkt != NULL);

    ptr = (uint8_t*)pkt;

    for(size_t i = 0; i < tokens.size(); i++)
        ptr[i] = atoi(tokens[i].c_str());

    assert(pkt->size == (size - 2));
    pkt->data[pkt->size - 1] = calculate_csum(pkt);

    return pkt;
}
